#include "TcpClient.h"
#include "Logger.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <cerrno>
#include <fcntl.h>      // 用于设置非阻塞
#include <sys/select.h> // 用于 select

namespace GiSo {

bool TcpClient::ConnectToServer(const std::string& ip, uint16_t port) {
    CloseSocket();

    m_Sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_Sockfd < 0) {
        GS_ERROR("Failed to create socket: {}", strerror(errno));
        return false;
    }

    // 设置非阻塞模式
    int flags = fcntl(m_Sockfd, F_GETFL, 0);
    if (flags == -1 || fcntl(m_Sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
        GS_ERROR("Failed to set non-blocking: {}", strerror(errno));
        CloseSocket();
        return false;
    }

    sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    if (inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr) <= 0) {
        GS_ERROR("Invalid IP address: {} ({})", ip, strerror(errno));
        CloseSocket();
        return false;
    }

    // 非阻塞连接
    int ret = connect(m_Sockfd, reinterpret_cast<sockaddr*>(&server_addr), sizeof(server_addr));
    if (ret < 0) {
        if (errno == EINPROGRESS) {
            // 连接正在进行中，使用 select 检查连接是否成功
            fd_set writefds;
            struct timeval tv;
            tv.tv_sec = 5;  // 5秒超时
            tv.tv_usec = 0;

            FD_ZERO(&writefds);
            FD_SET(m_Sockfd, &writefds);

            ret = select(m_Sockfd + 1, nullptr, &writefds, nullptr, &tv);
            if (ret <= 0) {  // 超时或错误
                GS_ERROR("Connect timeout or error: {}", strerror(errno));
                CloseSocket();
                return false;
            }

            // 检查 socket 是否有错误
            int error = 0;
            socklen_t len = sizeof(error);
            if (getsockopt(m_Sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error != 0) {
                GS_ERROR("Connection failed: {}", strerror(error));
                CloseSocket();
                return false;
            }
        } else {
            GS_ERROR("Connect failed: {}", strerror(errno));
            CloseSocket();
            return false;
        }
    }

    GS_INFO("Connected to {}:{}", ip, port);
    return true;
}

bool TcpClient::Wait(int timeoutMs) {  // 移除 const，因为需要修改 m_Sockfd
    if (m_Sockfd == -1) {
        GS_ERROR("Socket not initialized");
        return false;
    }

    fd_set readfds;
    struct timeval tv;
    tv.tv_sec = timeoutMs / 1000;
    tv.tv_usec = (timeoutMs % 1000) * 1000;

    FD_ZERO(&readfds);
    FD_SET(m_Sockfd, &readfds);

    int ret = select(m_Sockfd + 1, &readfds, nullptr, nullptr, &tv);
    if (ret < 0) {
        GS_ERROR("Select error: {}", strerror(errno));
        return false;
    }

    if (ret > 0 && FD_ISSET(m_Sockfd, &readfds)) {
        // 检查连接是否断开
        char buffer[1];
        ssize_t bytes = recv(m_Sockfd, buffer, 1, MSG_PEEK);  // 使用 MSG_PEEK 预览数据
        if (bytes == 0) {
            // 对端关闭了连接
            GS_ERROR("Connection closed by peer");
            close(m_Sockfd);
            m_Sockfd = -1;  // 更新 socket 状态
            return false;
        } else if (bytes < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
            // 接收错误（非非阻塞情况下的暂无数据）
            GS_ERROR("Recv error: {}", strerror(errno));
            close(m_Sockfd);
            m_Sockfd = -1;
            return false;
        }
        // bytes > 0 或非阻塞暂无数据，表示有数据可读
        return true;
    }
    return false;  // 超时或无数据
}

ssize_t TcpClient::SendData(const std::string& data) {
    if (m_Sockfd == -1) {
        GS_ERROR("Socket not initialized");
        return -1;
    }

    ssize_t totalSent = 0;
    const char* buffer = data.c_str();
    size_t remaining = data.size();

    while (remaining > 0) {
        ssize_t sent = send(m_Sockfd, buffer + totalSent, remaining, 0);
        if (sent < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // 缓冲区满，等待可写
                fd_set writefds;
                struct timeval tv{1, 0};  // 1秒超时
                FD_ZERO(&writefds);
                FD_SET(m_Sockfd, &writefds);

                if (select(m_Sockfd + 1, nullptr, &writefds, nullptr, &tv) <= 0) {
                    GS_ERROR("Send timeout");
                    return totalSent;
                }
                continue;
            }
            GS_ERROR("Send failed: {}", strerror(errno));
            return -1;
        }
        totalSent += sent;
        remaining -= sent;
    }
    return totalSent;
}

std::string TcpClient::ReceiveData(size_t maxBytes) {
    if (m_Sockfd == -1) {
        GS_ERROR("Socket not initialized");
        return "";
    }

    std::string result;
    result.reserve(maxBytes);
    char buffer[1024];

    ssize_t bytes = recv(m_Sockfd, buffer, std::min(sizeof(buffer) - 1, maxBytes), 0);
    if (bytes > 0) {
        buffer[bytes] = '\0';
        result = buffer;
    } else if (bytes == 0) {
        GS_ERROR("Connection closed by server");
        CloseSocket();
    } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
        GS_ERROR("Receive failed: {}", strerror(errno));
    }
    return result;
}

void TcpClient::Disconnect() {
    CloseSocket();
}

void TcpClient::CloseSocket() {
    if (m_Sockfd != -1) {
        close(m_Sockfd);
        m_Sockfd = -1;
    }
}

} // namespace GiSo